package com.atguigu.gulimall.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.to.mq.OrderTo;
import com.atguigu.common.to.mq.SeckillOrderTo;
import com.atguigu.common.utils.Query;
import com.atguigu.common.utils.R;
import com.atguigu.common.vo.MemberRespVo;
import com.atguigu.gulimall.order.constant.OrderConstant;
import com.atguigu.gulimall.order.entity.OrderItemEntity;
import com.atguigu.gulimall.order.entity.PaymentInfoEntity;
import com.atguigu.gulimall.order.enume.OrderStatusEnum;
import com.atguigu.gulimall.order.feign.CartFeignService;
import com.atguigu.gulimall.order.feign.MemberFeignService;
import com.atguigu.gulimall.order.feign.WmsFeignService;
import com.atguigu.gulimall.order.feign.productFeignService;
import com.atguigu.gulimall.order.interceptor.LoginUserInterceptor;
import com.atguigu.gulimall.order.service.OrderItemService;
import com.atguigu.gulimall.order.service.PaymentInfoService;
import com.atguigu.gulimall.order.to.OrderCreateTo;
import com.atguigu.gulimall.order.vo.*;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;

import io.seata.spring.annotation.GlobalTransactional;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;

import com.atguigu.gulimall.order.dao.OrderDao;
import com.atguigu.gulimall.order.entity.OrderEntity;
import com.atguigu.gulimall.order.service.OrderService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

@Service
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    private ThreadLocal<OrderSubmitVo> orderSubmitVoThreadLocal=new ThreadLocal<>();

    @Autowired
    ThreadPoolExecutor executor;

    @Autowired
    MemberFeignService memberFeignService;

    @Autowired
    CartFeignService cartFeignService;
    
    @Autowired
    WmsFeignService wmsFeignService;
    
    @Autowired
    productFeignService productFeignService;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    OrderItemService orderItemService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    PaymentInfoService paymentInfoService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
        //会员信息
        MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();

        OrderConfirmVo confirmVo = new OrderConfirmVo();

        /**
         * 如果为异步请求，有多个线程，拿不到当前请求线程的数据
         * 因为RequestContextHolder中的信息是用ThreadLocal存储的，ThreadLocal中填充的变量属于当前线程
         * 所有异步执行中其他线程拿不到当前线程ThreadLocal的数据
         * 
         * 下面的异步线程会等到主线程执行到这里后才会执行
         */
        //获取当前的请求数据
        System.out.println("主线程。。。"+Thread.currentThread().getId());
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        CompletableFuture<Void> getAddressFuture = CompletableFuture.runAsync(() -> {
            //每一个线程都来共享当前的请求数据
            RequestContextHolder.setRequestAttributes(requestAttributes);
            System.out.println("address线程。。。"+Thread.currentThread().getId());
            //远程调用所有收货地址列表
            List<MemberAddressVo> address = memberFeignService.getAddress(memberRespVo.getId());
            confirmVo.setAddress(address);
        }, executor);

        CompletableFuture<Void> cartFuture = CompletableFuture.runAsync(() -> {
            //每一个线程都来共享当前的请求数据
            RequestContextHolder.setRequestAttributes(requestAttributes);
            System.out.println("cart线程。。。"+Thread.currentThread().getId());
            //远程查询购物车所有选中的购物项
            List<OrderItemVo> currentUserCartItems = cartFeignService.getCurrentUserCartItems();

            confirmVo.setItems(currentUserCartItems);
        }, executor).thenRunAsync(()->{
            List<OrderItemVo> items = confirmVo.getItems();
            List<Long> collect = items.stream().map(OrderItemVo::getSkuId).collect(Collectors.toList());
            R skusHasStock = wmsFeignService.getSkusHasStock(collect);
            List<SkuStockVo> data = skusHasStock.getData(new TypeReference<List<SkuStockVo>>() {
            });
            if (data!=null){
                Map<Long, Boolean> map = data.stream().collect(Collectors.toMap(SkuStockVo::getSkuId, SkuStockVo::getHasStock));
                confirmVo.setStocks(map);
            }
        },executor);

        //查询用户积分
        Integer integration = memberRespVo.getIntegration();

        confirmVo.setIntegration(integration);

        //其他数据自动计算
        //TODO 防重令牌
        String token = UUID.randomUUID().toString().replace("_", "");
        redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN+memberRespVo.getId(),token,30, TimeUnit.MINUTES);
        confirmVo.setOrderToken(token);
        CompletableFuture.allOf(getAddressFuture,cartFuture).get();

        return confirmVo;
    }


    //本地事务，在分布式系统中，只能控制自己回滚，控制不了其他服务的回滚
    //分布式事务，最大原因 网络问题
//    @GlobalTransactional
    @Transactional
    @Override
    public SubmitOrderResponseVo submitOrder(OrderSubmitVo orderSubmitVo) {
        //下单：创建订单，验令牌，验价格，锁库存
        SubmitOrderResponseVo response = new SubmitOrderResponseVo();
        //会员信息
        MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
        //1.验证令牌【令牌的对比和删除必须保证原子性 0令牌失败 1令牌成功】
        String script="if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Long eval = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Collections.singletonList(OrderConstant.USER_ORDER_TOKEN + memberRespVo.getId()), Collections.singletonList(orderSubmitVo.getOrderToken()));
        if (eval==0){
            response.setCode(1);
            return response;
        }
        orderSubmitVoThreadLocal.set(orderSubmitVo);
        //创建订单
        OrderCreateTo order = createOrder();
        //验价 金额对比
        BigDecimal payAmount = order.getOrder().getPayAmount();
        BigDecimal payPrice = orderSubmitVo.getPayPrice();
        if (Math.abs(payAmount.subtract(payPrice).doubleValue())>=0.01){
            response.setCode(2);
            return response;
        }

        //TODO 保存订单
        saveOrder(order);
        //库存锁定,只要有异常回滚订单数据
        WareSkuLockVo lockVo=new WareSkuLockVo();
        lockVo.setOrderSn(order.getOrder().getOrderSn());
        List<OrderItemVo> locks = order.getOrderItem().stream().map(item -> {
            OrderItemVo itemVo = new OrderItemVo();
            itemVo.setSkuId(item.getSkuId());
            itemVo.setCount(item.getSkuQuantity());
            itemVo.setTitle(item.getSkuName());
            return itemVo;
        }).collect(Collectors.toList());
        lockVo.setLocks(locks);
        //TODO 远程锁库存

        //为保证高并发，库存服务自己回滚 可以发消息给库存系统
        //库存服务本身也可以使用自动解锁模式 消息
        R r = wmsFeignService.orderLockStock(lockVo);
        if (r.getCode()!=0){
            throw new RuntimeException("--没有足够的库存");
//            response.setCode(3);
//            return response;
        }

        //TODO 远程扣减积分 出异常  订单会回滚 远程的锁库存不会回滚
        //int i=10/0;
        //TODO 订单创建成功  发送消息
        rabbitTemplate.convertAndSend("order-event-exchange","order.create.order",order.getOrder());

        response.setCode(0);
        response.setOrder(order.getOrder());
        return response;
    }

    @Override
    public OrderEntity getOrderStatus(String orderSn) {

        OrderEntity orderEntity = baseMapper.selectOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
        return orderEntity;
    }

    @Override
    public void closeOrder(OrderEntity entity) {
        //该订单未支付，直接走下面逻辑
        // 该订单正在支付，异步还没回调，也走下面逻辑，回调后会改相应的状态
        // 如果该订单正在支付，异步正在回调，还没来得急修改状态下面if条件已成立，这是异步先执行了修改状态，那么下面逻辑又会修改状态为已关闭

        //查询当前这个订单的最新状态
        OrderEntity orderEntity = baseMapper.selectById(entity.getId());

        //有可能库存服务出现异常和订单消息发送出去后宕机了，导致订单数据回滚
        if (orderEntity!=null){
            if (orderEntity.getStatus()==OrderStatusEnum.CREATE_NEW.getCode()){//新建状态
                // TODO 调用支付宝统一收单线下交易查询，具体状态已支付宝返回状态为准
                    //TODO 交易成功(TRADE_SUCCESS) 就修改订单状态为已完成    然后发送消息给库存服务，修改状态为扣减或者直接修改
                    //TODO WAIT_BUYER_PAY（交易创建，等待买家付款） 调用统一收单交易关闭接口 ==》如果返回不能关闭，说明正在支付 可以选择等待几秒后进行自旋 如果关闭成功则执行下面逻辑

                //关单
                OrderEntity update = new OrderEntity();
                update.setStatus(OrderStatusEnum.CANCLED.getCode());
                update.setId(orderEntity.getId());
                baseMapper.updateById(update);
                OrderTo orderTo = new OrderTo();
                BeanUtils.copyProperties(orderEntity,orderTo);
                //下订单成功后，由于网络的不稳定，发出消息时延时了比较久。就会导致解锁库存消息执行比解锁订单的消息快。库存消息优先到期
                //那么就会造成去解锁库存时查询到订单不是取消状态，而是创建状态就不会解锁库存还把消息消费了
                //所有订单解锁完后应该"主动"将解锁成功的消息发送给MQ，让库存重新去解锁

                //TODO 为保证消息百分百发送成功,消息不丢失,在每次发送消息的时候做好日志记录(先给数据库保存每一个消息的详细信息，在发送消息)
                //TODO 定时器定时扫描数据库将失败的消息在发送一遍
                //TODO 在rabbitTemplate.setConfirmCallback和rabbitTemplate.setReturnCallback设置对应的状态信息
                //TODO 消息的重复消费 设计消费消息的业务为幂等性
                //TODO 手动发送ack机制告诉消息队列消息已消费 channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
                //      拒绝消息 告诉服务器消息未消费，true为重新放到队列中，让别人继续消费
                //      channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);
                //TODO 消息的积压 限流 上线更多消费者
                //模拟消息表的id
                CorrelationData correlationData = new CorrelationData("消息表的id");
                rabbitTemplate.convertAndSend("order-event-exchange","stock.release.other",orderTo,correlationData);
            }
        }
    }

    @Override
    public PayVo getOrderPay(String orderSn) {
        PayVo payVo = new PayVo();
        OrderEntity orderEntity = baseMapper.selectOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
        BigDecimal bigDecimal = orderEntity.getPayAmount().setScale(2, BigDecimal.ROUND_UP);
        payVo.setTotal_amount(bigDecimal.toString());
        payVo.setOut_trade_no(orderEntity.getOrderSn());
        payVo.setSubject("谷粒商城支付");
        payVo.setBody("谷粒商城支付");
        return payVo;
    }

    @Override
    public PageUtils queryPageWithItem(Map<String, Object> params) {
        MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();

        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>().eq("member_id",memberRespVo.getId()).orderByDesc("id")
        );

        List<OrderEntity> order_sn = page.getRecords().stream().map(order -> {
            List<OrderItemEntity> itemEntities = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", order.getOrderSn()));
            order.setItemEntities(itemEntities);
            return order;
        }).collect(Collectors.toList());
        page.setRecords(order_sn);

        return new PageUtils(page);
    }

    /**
     * 处理支付宝的处理结果
     * @param vo
     * @return
     */
    @Override
    public String handlePayResult(PayAsyncVo vo) {
        //保存交易流水
        PaymentInfoEntity paymentInfoEntity = new PaymentInfoEntity();
        paymentInfoEntity.setAlipayTradeNo(vo.getOut_trade_no());
        paymentInfoEntity.setOrderSn(vo.getTrade_no());
        paymentInfoEntity.setPaymentStatus(vo.getTrade_status());
        paymentInfoEntity.setCallbackTime(vo.getNotify_time());

        paymentInfoService.save(paymentInfoEntity);

        //修改订单状态信息
        if (vo.getTrade_status().equals("TRADE_SUCCESS") || vo.getTrade_status().equals("TRADE_FINISHED")){
            //支付成功
            String outTradeNo = vo.getOut_trade_no();
            baseMapper.updateOrderStatus(outTradeNo,OrderStatusEnum.PAYED.getCode());

            // TODO 发送消息给库存服务，修改状态为扣减或者直接修改
        }
        return "success";
    }

    @Override
    public void createSeckillOrder(SeckillOrderTo seckillOrderTo) {
        //TODO 保存秒杀订单信息
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(seckillOrderTo.getOrderSn());
        orderEntity.setMemberId(seckillOrderTo.getMemberId());
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        BigDecimal multiply = seckillOrderTo.getSeckillPrice().multiply(new BigDecimal(seckillOrderTo.getNum() + ""));
        orderEntity.setPayAmount(multiply);
        baseMapper.insert(orderEntity);

        //TODO 保存秒杀订单项信息
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        orderItemEntity.setOrderSn(seckillOrderTo.getOrderSn());
        orderItemEntity.setRealAmount(multiply);
        orderItemEntity.setSkuQuantity(seckillOrderTo.getNum());
        orderItemService.save(orderItemEntity);
    }

    /**
     * 保存订单数据
     * @param order
     */
    private void saveOrder(OrderCreateTo order) {
        OrderEntity orderEntity = order.getOrder();
        orderEntity.setModifyTime(new Date());
        baseMapper.insert(orderEntity);

        List<OrderItemEntity> orderItem = order.getOrderItem();
        orderItemService.saveBatch(orderItem);
    }

    private OrderCreateTo createOrder(){
        OrderCreateTo orderCreateTo = new OrderCreateTo();
        //生成订单号
        String orderSn = IdWorker.getTimeId();
        //构建订单
        OrderEntity orderEntity = buildOrder(orderSn);
        //获取所有的订单项
        List<OrderItemEntity> orderItemEntities = buildOrderItem(orderSn);

        //计算价格，积分相关信息
        computePrice(orderEntity,orderItemEntities);

        orderCreateTo.setOrder(orderEntity);
        orderCreateTo.setOrderItem(orderItemEntities);
        return orderCreateTo;
    }

    private void computePrice(OrderEntity orderEntity, List<OrderItemEntity> orderItemEntities) {
        //订单总额
        BigDecimal total = new BigDecimal("0.0");
        BigDecimal coupon = new BigDecimal("0.0");
        BigDecimal integration = new BigDecimal("0.0");
        BigDecimal promotion = new BigDecimal("0.0");
        for (OrderItemEntity orderItemEntity : orderItemEntities) {
            coupon=coupon.add(orderItemEntity.getCouponAmount());
            integration=integration.add(orderItemEntity.getIntegrationAmount());
            promotion=promotion.add(orderItemEntity.getPromotionAmount());
            total=total.add(orderItemEntity.getRealAmount());
        }
        //订单价格相关
        orderEntity.setTotalAmount(total);
        //应付总额
        orderEntity.setPayAmount(total.add(orderEntity.getFreightAmount()));
        orderEntity.setPromotionAmount(promotion);
        orderEntity.setIntegrationAmount(promotion);
        orderEntity.setCouponAmount(promotion);
    }

    //构建订单
    private OrderEntity buildOrder(String orderSn){
        MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
        OrderEntity orderEntity = new OrderEntity();
        //获取收货地址信息
        OrderSubmitVo orderSubmitVo = orderSubmitVoThreadLocal.get();
       R fare = wmsFeignService.getFare(orderSubmitVo.getAddrId());
        FareVo fareResp= fare.getData(new TypeReference<FareVo>() {
        });

        //设置订单号
       orderEntity.setOrderSn(orderSn);
        //设置运费
        orderEntity.setFreightAmount(fareResp.getFare());
        //设置收货人信息
        orderEntity.setMemberId(memberRespVo.getId());
        orderEntity.setReceiverProvince(fareResp.getAddress().getProvince());
        orderEntity.setReceiverPostCode(fareResp.getAddress().getPostCode());
        orderEntity.setReceiverPhone(fareResp.getAddress().getPhone());
        orderEntity.setReceiverName(fareResp.getAddress().getName());
        orderEntity.setReceiverCity(fareResp.getAddress().getCity());
        orderEntity.setReceiverRegion(fareResp.getAddress().getRegion());

        //设置订单相关状态信息
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        orderEntity.setAutoConfirmDay(7);

        return orderEntity;
    }

    //构建所有的订单项
    private List<OrderItemEntity> buildOrderItem(String orderSn) {
        //获取所有的订单项[redis中获取]
        List<OrderItemVo> currentUserCartItems = cartFeignService.getCurrentUserCartItems();
        if (currentUserCartItems!=null && currentUserCartItems.size()>0){
            List<OrderItemEntity> collect = currentUserCartItems.stream().map(item -> {
                OrderItemEntity orderItemEntity = new OrderItemEntity();
                //1.订单信息
                orderItemEntity.setOrderSn(orderSn);
                //2.商品的spu信息
                Long skuId = item.getSkuId();
                R spuInfo = productFeignService.getSpuInfoBySkuId(skuId);
                SpuInfoVo data = spuInfo.getData(new TypeReference<SpuInfoVo>() {
                });
                orderItemEntity.setSpuId(data.getId());
                orderItemEntity.setSpuBrand(data.getBrandId().toString());
                orderItemEntity.setSpuName(data.getSpuName());
                orderItemEntity.setCategoryId(data.getCatalogId());
                //3.商品的sku信息
                orderItemEntity.setSkuId(item.getSkuId());
                orderItemEntity.setSkuName(item.getTitle());
                orderItemEntity.setSkuPic(item.getImages());
                orderItemEntity.setSkuPrice(item.getPrice());
                String skuAttr = StringUtils.join(item.getSkuAttr(), ";");
                orderItemEntity.setSkuAttrsVals(skuAttr);
                orderItemEntity.setSkuQuantity(item.getCount());
                //4.优惠信息
                //5.积分信息
                orderItemEntity.setGiftGrowth(item.getPrice().intValue());
                orderItemEntity.setGiftIntegration(item.getPrice().intValue());
                //价格信息
                orderItemEntity.setPromotionAmount(new BigDecimal("0"));//打折
                orderItemEntity.setCouponAmount(new BigDecimal("0"));//优惠
                //当前订单项的实际金额
                BigDecimal orign = orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity().toString()));
                BigDecimal subtract = orign.subtract(orderItemEntity.getCouponAmount()).subtract(orderItemEntity.getPromotionAmount()).subtract(orderItemEntity.getIntegrationAmount());
                orderItemEntity.setRealAmount(subtract);
                return orderItemEntity;
            }).collect(Collectors.toList());

            return collect;
        }
       return null;
    }

}